#! /usr/bin/env python
# -*- coding:utf-8 -*-
# @Time : 2020/10/5 23:09
# @Author : youngchaolin

''''''
'''
装饰器：类似房子装修后可以体验更多的功能
开放封闭原则
开放：对代码的拓展是开放的
封闭：对源码的修改是封闭的
'''

# 装饰器，完全遵循开放封闭原则
# 装饰器，在不改变原函数代码以及调用方式的前提下，为其增加的新功能
# 装饰器就是一个函数，python中可以传入函数名做为参数，使得装饰器比java中装饰者模式看起来更加高级



# 1 统计一段程序的运行时间，使用装饰器实现
import time

def index():
    '''一段代码'''
    time.sleep(2)
    print('欢迎来到博客园')
# timer就是一个函数，也是一个装饰器
def timer(f):
    def inner():
        start_time=time.time()
        f()
        end_time=time.time()
        print(f'程序运行时间为{end_time-start_time}')
    return inner


index=timer(index)
print(index.__code__.co_freevars)
# 执行，调用的方式没有改变，也没有改变原函数的代码
index()

# 2 另外的版本
# timer就是一个函数，也是一个装饰器，本质上是一个闭包
# def timer(f):
#     # 内部函数inner有对外部函数变量f的引用，为闭包
#     f=index
#     def inner():
#         start_time=time.time()
#         f()
#         end_time=time.time()
#         print(f'程序运行时间为{end_time-start_time}')
#
#     return inner
#
# index=timer(index)
# print(index.__code__.co_freevars)
# # 执行，调用的方式没有改变，也没有改变原函数的代码
# index()

# 3 另外的版本，标准版的装饰器
# 如果需装饰的函数比较多，函数名=timer(函数名)可能会写很多次，会显得麻烦
# python做了一个优化，提出了语法糖的概念

# def index():
#     '''一段代码'''
#     time.sleep(2)
#     print('欢迎来到博客园')
#
# def dairy():
#     '''一段代码'''
#     time.sleep(3)
#     print('欢迎来到日记页面')

# 这样写很麻烦，使用@timer可以解决问题
# index=timer(index)
# index()
# dairy=timer(dairy)
# dairy()

@timer
def index():
    '''一段代码'''
    time.sleep(2)
    print('欢迎来到博客园')
@timer
def dairy():
    '''一段代码'''
    time.sleep(3)
    print('欢迎来到日记页面')

index()
dairy()


# 4 被装饰的函数，如果有返回值
print('----被装饰函数有返回值----')
@timer
def index():
    '''一段代码'''
    time.sleep(2)
    print('欢迎来到博客园')
    return 666
ret=index()
print(ret) # None，返回值其实就是inner()函数返回值，要想把666返回，需要修改timer函数


def timer(f):
    def inner():
        start_time=time.time()
        # index函数有返回值，这里也要执行，后面返回
        ret=f()
        end_time=time.time()
        print(f'程序运行时间为{end_time-start_time}')
        return ret
    return inner
print('----被装饰函数有返回值,修改timer函数----')

# 加不加@timer，都要返回666，为了不影响结果，需要借助inner函数将index函数的返回值返回
@timer
def index():
    '''一段代码'''
    time.sleep(2)
    print('欢迎来到博客园')
    return 666
ret=index()
print(ret)

# 5 被装饰函数带参数
print('-----被装饰函数带参数-----')

def timer(f):
    def inner(name):
        start_time=time.time()
        # index函数有返回值，这里也要执行，后面返回
        ret=f(name)
        end_time=time.time()
        print(f'程序运行时间为{end_time-start_time}')
        return ret
    return inner
@timer
def index(name):
    '''一段代码'''
    time.sleep(2)
    print(f'欢迎{name}来到博客园')
    return 666

ret=index('clyang')
print(ret)

def dairy(name,age):
    '''一段代码'''
    time.sleep(3)
    print(f'欢迎{age}岁的{name}来到日记页面')
# dairy函数有两个函数，需要修改timer函数
dairy('李嘉欣',18)

# def timer(f):
#     def inner(name,age):
#         start_time=time.time()
#         f(name,age)
#         end_time=time.time()
#         print(f'程序运行时间为{end_time-start_time}')
#     return inner
# 重新定义timer方法
def timer(f):
    def inner(*args,**kwargs):
        start_time=time.time()
        # args是一个元祖，执行f函数时，参数会被打散
        f(*args,**kwargs)
        end_time=time.time()
        print(f'程序运行时间为{end_time-start_time}')
    return inner

@timer
def dairy(name,age):
    '''一段代码'''
    time.sleep(3)
    print(f'欢迎{age}岁的{name}来到日记页面')
# 对应也要修改timer函数

dairy('李嘉欣',18)

# 6 标准的装饰器，核心代码部分就是5行
def wrapper(f):
    def inner(*args,**kwargs):
        '''添加额外功能，执行被装饰函数之前的功能'''
        ret=f(*args,**kwargs)
        '''添加额外功能，执行被装饰函数之后的功能'''
        return ret
    return inner

